#include "fasterdom.h"

char __FASTERDOM_VERSION[] = "0.0.8.0" ;
char *__FASTERDOM_VERSION_0_0_8_0 = __FASTERDOM_VERSION ;

__thread int	g_paster_last_error = 0 ;

#ifdef _WIN32
char *strndup (const char *s, size_t n)
{
	char *result;
	size_t len = strlen (s);

	if (len > n)
		len = n;

	result = (char *) malloc (len + 1);
	if (!result)
		return 0;

	result[len] = '\0';
	return (char *) memcpy (result, s, len);
}
#endif

struct DomNode
{
	struct DomNode		*xml_declaration ;
	struct DomNode		*xml_properties ;
	unsigned char		is_xml_cdata_node ;
	
	struct DomNode		*parent_node ;
	
	struct DomNode		*prev_node ;
	
	char			*node_name ;
	int			node_name_len ;
	enum DomNodeType	node_type ;
	unsigned char		is_node_data_exist ;
	union
	{
		char		s_buf[ DOMNODE_STRINGCACHE_SIZE ] ;
		long		l_buf ;
		double		d_buf ;
		unsigned char	b_buf ;
	} node_data ;
	char			*node_data_s_ptr ;
	int			node_data_len ;
	
	struct DomNode		*next_node ;
	
	struct DomNode		*children_nodes ;
} ;

struct DomNode *CreateDomNode( char *node_name , int node_name_len , enum DomNodeType node_type , void *node_data , int node_data_len )
{
	struct DomNode	*node = NULL ;
	
	node = (struct DomNode *)malloc( sizeof(struct DomNode) ) ;
	if( node == NULL )
		return NULL;
	memset( node , 0x00 , sizeof(struct DomNode) );
	
	if( node_name )
	{
		if( node_name_len >= 0 )
			node->node_name = strndup( node_name , node_name_len ) ;
		else if( node_name_len == CREATEDOMNODE_AUTOMATIC_LENGTH )
			node->node_name = strdup( node_name ) ;
		else if( node_name_len == CREATEDOMNODE_REFERENCE )
			node->node_name = node_name ;
		else if( node_name_len == CREATEDOMNODE_REFERENCE_AND_DELEGATION_OF_FREE )
			node->node_name = node_name ;
		else
		{
			free( node );
			return NULL;
		}
		
		node->node_name_len = node_name_len ;
	}
	node->node_type = node_type ;
	if( node_data )
	{
		switch( node->node_type )
		{
			case DOMNODE_TYPE_STRING :
				if( node_data_len >= CREATEDOMNODE_AUTOMATIC_LENGTH )
				{
					if( node_data_len == CREATEDOMNODE_AUTOMATIC_LENGTH )
						node_data_len = (int)strlen(node_data) ;
					
					if( node_data_len < sizeof(node->node_data.s_buf) )
					{
						memcpy( node->node_data.s_buf , node_data , node_data_len );
						node->node_data_s_ptr = node->node_data.s_buf ;
					}
					else
					{
						node->node_data_s_ptr = strndup( (char*)node_data , node_data_len ) ;
						if( node->node_data_s_ptr == NULL )
						{
							DestroyDomNode( node );
							return NULL;
						}
					}
				}
				else if( node_data_len == CREATEDOMNODE_REFERENCE )
				{
					node->node_data_s_ptr = node_data ;
				}
				else if( node_data_len == CREATEDOMNODE_REFERENCE_AND_DELEGATION_OF_FREE )
				{
					node->node_data_s_ptr = node_data ;
				}
				else
				{
					DestroyDomNode( node );
					return NULL;
				}
				
				node->node_data_len = node_data_len ;
				
				break;
			case DOMNODE_TYPE_LONG :
				node->node_data.l_buf = *((long*)node_data) ;
				break;
			case DOMNODE_TYPE_DOUBLE :
				node->node_data.d_buf = *((double*)node_data) ;
				break;
			case DOMNODE_TYPE_BOOL :
				node->node_data.b_buf = *((unsigned char *)node_data) ;
				break;
			default :
				return NULL;
		}
		node->is_node_data_exist = 1 ;
	}
	
	return node;
}

struct DomNode *LinkSiblingDomNode( struct DomNode *prev_node , struct DomNode *node )
{
	struct DomNode	*last_node = NULL ;
	
	if( prev_node == NULL || node == NULL )
		return NULL;
	
	last_node = GetLastDomNode( prev_node ) ;
	last_node->next_node = node ;
	node->prev_node = last_node ;
	node->parent_node = last_node->parent_node ;
	if( last_node->parent_node )
		last_node->parent_node->children_nodes->prev_node = node ;
	
	return node;
}

struct DomNode *LinkChildDomNode( struct DomNode *parent_node , struct DomNode *node )
{
	if( parent_node == NULL || node == NULL )
		return NULL;
	
	if( parent_node->children_nodes == NULL )
	{
		node->parent_node = parent_node ;
		node->prev_node = node ;
		parent_node->children_nodes = node ;
		return node;
	}
	else
	{
		LinkSiblingDomNode( parent_node->children_nodes , node );
		return node;
	}
}

void UnlinkDomNode( struct DomNode *node )
{
	if( node == NULL )
		return;
	
	if( node->parent_node )
	{
		if( node->prev_node && node != node->parent_node->children_nodes )
			node->prev_node->next_node = node->next_node ;
	}
	else
	{
		if( node->prev_node )
			node->prev_node->next_node = node->next_node ;
	}
	
	if( node->next_node )
		node->next_node->prev_node = node->prev_node ;
	
	node->parent_node = NULL ;
	node->prev_node = NULL ;
	node->next_node = NULL ;
	
	return;
}

void DestroyDomNode( struct DomNode *node )
{
	if( node == NULL )
		return;
	
	if( node->xml_declaration )
	{
		DestroyAllDomNodes( node->xml_declaration );
	}
	
	if( node->xml_properties )
	{
		DestroyAllDomNodes( node->xml_properties );
	}
	
	if( node->children_nodes )
		DestroyAllDomNodes( node->children_nodes );
	
	UnlinkDomNode( node );
	
	if( node->node_name )
	{
		if( node->node_name_len >= CREATEDOMNODE_AUTOMATIC_LENGTH )
			free( node->node_name );
		else if( node->node_name_len == CREATEDOMNODE_REFERENCE_AND_DELEGATION_OF_FREE )
			free( node->node_name );
	}
	
	switch( node->node_type )
	{
		case DOMNODE_TYPE_STRING :
			if( node->node_data_len >= CREATEDOMNODE_AUTOMATIC_LENGTH )
			{
				if( node->node_data_s_ptr != node->node_data.s_buf )
					free( node->node_data_s_ptr );
			}
			else if( node->node_data_len == CREATEDOMNODE_REFERENCE_AND_DELEGATION_OF_FREE )
			{
				free( node->node_data_s_ptr );
			}
			break;
		case DOMNODE_TYPE_LONG :
			break;
		case DOMNODE_TYPE_DOUBLE :
			break;
		case DOMNODE_TYPE_BOOL :
			break;
		default :
			break;
	}
	
	free( node );
	
	return;
}

void DestroyAllDomNodes( struct DomNode *nodes )
{
	struct DomNode	*second_node = NULL ;
	struct DomNode	*second_next_node = NULL ;
	
	if( nodes == NULL )
		return;
	
	second_node = nodes->next_node ;
	while( second_node )
	{
		second_next_node = second_node->next_node ;
		
		DestroyDomNode( second_node );
		
		second_node = second_next_node ;
	}
	
	DestroyDomNode( nodes );
	
	return;
}

struct DomNode *GetFirstDomNode( struct DomNode *node )
{
	struct DomNode	*last_node = NULL ;
	struct DomNode	*first_node = NULL ;
	
	if( node == NULL )
		return NULL;
	
	if( node->parent_node )
		return node->parent_node->children_nodes;
	
	last_node = node ;
	while( last_node->next_node )
		last_node = last_node->next_node;
	
	first_node = node ;
	while( first_node->prev_node == last_node )
		first_node = first_node->prev_node;
	
	return first_node;
}

struct DomNode *GetLastDomNode( struct DomNode *node )
{
	struct DomNode	*last_node = NULL ;
	
	if( node == NULL )
		return NULL;
	
	if( node->parent_node )
		return node->parent_node->children_nodes->prev_node;
	
	last_node = node ;
	while( last_node->next_node )
		last_node = last_node->next_node;
	
	return last_node;
}

struct DomNode *GetPrevDomNode( struct DomNode *node )
{
	if( node == NULL )
		return NULL;
	
	if( node->parent_node && node->parent_node->children_nodes == node )
		return NULL;
	else
		return node->prev_node;
}

struct DomNode *GetNextDomNode( struct DomNode *node )
{
	if( node == NULL )
		return NULL;
	
	return node->next_node;
}

struct DomNode *GetChildrenDomNodes( struct DomNode *node )
{
	if( node == NULL )
		return NULL;
	
	return node->children_nodes;
}

struct DomNode *GetParentDomNode( struct DomNode *node )
{
	if( node == NULL )
		return NULL;
	
	return node->parent_node;
}

char *GetDomNodeName( struct DomNode *node )
{
	return node->node_name;
}

enum DomNodeType GetDomNodeType( struct DomNode *node )
{
	return node->node_type;
}

enum DomNodeExist GetDomNodeDataExist( struct DomNode *node )
{
	if( node->is_node_data_exist == 0 )
		return DOMNODE_NODE_DATA_NOT_EXIST;
	else
		return DOMNODE_NODE_DATA_EXIST;
}

struct DomNode *FindChildDomNode( struct DomNode *parent_node , char *node_name )
{
	struct DomNode	*child_node = NULL ;
	
	for( child_node = parent_node->children_nodes ; child_node ; child_node = child_node->next_node )
	{
		if( ( child_node->node_name == NULL && node_name == NULL ) || strcmp( child_node->node_name , node_name ) == 0 )
		{
			return child_node;
		}
	}
	
	return NULL;
}

struct DomNode *FindSiblingDomNode( struct DomNode *node , char *node_name )
{
	struct DomNode	*sibling_node = NULL ;
	
	for( sibling_node = GetFirstDomNode(node) ; sibling_node ; sibling_node = sibling_node->next_node )
	{
		if( ( sibling_node->node_name == NULL && node_name == NULL ) || strcmp( sibling_node->node_name , node_name ) == 0 )
		{
			return sibling_node;
		}
	}
	
	return NULL;
}

void SetDomNodeXmlDeclaration( struct DomNode *node , struct DomNode *xml_declaration )
{
	node->xml_declaration = xml_declaration ;
	return;
}

struct DomNode *GetDomNodeXmlDeclarationPtr( struct DomNode *node )
{
	return node->xml_declaration;
}

struct DomNode *CreateRootDomNode( char *node_name , int node_name_len )
{
	return CreateDomNode( node_name , node_name_len , DOMNODE_TYPE_BRANCH , NULL , -1 );
}

struct DomNode *AddSiblingDomNode_BRANCH( struct DomNode *prev_node , char *node_name , int node_name_len )
{
	return LinkSiblingDomNode( prev_node , CreateDomNode(node_name,node_name_len,DOMNODE_TYPE_BRANCH,NULL,-1) );
}

struct DomNode *AddSiblingDomNode_ARRAY( struct DomNode *prev_node , char *node_name , int node_name_len )
{
	return LinkSiblingDomNode( prev_node , CreateDomNode(node_name,node_name_len,DOMNODE_TYPE_ARRAY,NULL,-1) );
}

struct DomNode *AddSiblingDomNode_string( struct DomNode *prev_node , char *node_name , int node_name_len , char *s , int len )
{
	return LinkSiblingDomNode( prev_node , CreateDomNode(node_name,node_name_len,DOMNODE_TYPE_STRING,s,len) );
}

struct DomNode *AddSiblingDomNode_long( struct DomNode *prev_node , char *node_name , int node_name_len , long l )
{
	return LinkSiblingDomNode( prev_node , CreateDomNode(node_name,node_name_len,DOMNODE_TYPE_LONG,&l,-1) );
}

struct DomNode *AddSiblingDomNode_double( struct DomNode *prev_node , char *node_name , int node_name_len , double d )
{
	return LinkSiblingDomNode( prev_node , CreateDomNode(node_name,node_name_len,DOMNODE_TYPE_DOUBLE,&d,-1) );
}

struct DomNode *AddSiblingDomNode_bool( struct DomNode *prev_node , char *node_name , int node_name_len , unsigned char b )
{
	return LinkSiblingDomNode( prev_node , CreateDomNode(node_name,node_name_len,DOMNODE_TYPE_BOOL,&b,-1) );
}

struct DomNode *AddChildDomNode_BRANCH( struct DomNode *parent_node , char *node_name , int node_name_len )
{
	return LinkChildDomNode( parent_node , CreateDomNode(node_name,node_name_len,DOMNODE_TYPE_BRANCH,NULL,-1) );
}

struct DomNode *AddChildDomNode_ARRAY( struct DomNode *parent_node , char *node_name , int node_name_len )
{
	return LinkChildDomNode( parent_node , CreateDomNode(node_name,node_name_len,DOMNODE_TYPE_ARRAY,NULL,-1) );
}

struct DomNode *AddChildDomNode_string( struct DomNode *parent_node , char *node_name , int node_name_len , char *s , int len )
{
	return LinkChildDomNode( parent_node , CreateDomNode(node_name,node_name_len,DOMNODE_TYPE_STRING,s,len) );
}

struct DomNode *AddChildDomNode_long( struct DomNode *parent_node , char *node_name , int node_name_len , long l )
{
	return LinkChildDomNode( parent_node , CreateDomNode(node_name,node_name_len,DOMNODE_TYPE_LONG,&l,-1) );
}

struct DomNode *AddChildDomNode_double( struct DomNode *parent_node , char *node_name , int node_name_len , double d )
{
	return LinkChildDomNode( parent_node , CreateDomNode(node_name,node_name_len,DOMNODE_TYPE_DOUBLE,&d,-1) );
}

struct DomNode *AddChildDomNode_bool( struct DomNode *parent_node , char *node_name , int node_name_len , unsigned char b )
{
	return LinkChildDomNode( parent_node , CreateDomNode(node_name,node_name_len,DOMNODE_TYPE_BOOL,&b,-1) );
}

char *GetDomNodeData_string( struct DomNode *node , enum DomNodeExist *is_exist )
{
	if( node->node_type != DOMNODE_TYPE_STRING )
	{
		if( is_exist )
			(*is_exist) = DOMNODE_NODE_TYPE_NOT_MATCHED ;
		return NULL;
	}
	
	if( node->is_node_data_exist == 0 )
	{
		if( is_exist )
			(*is_exist) = DOMNODE_NODE_DATA_NOT_EXIST ;
		return NULL;
	}
	
	if( is_exist )
		(*is_exist) = DOMNODE_NODE_DATA_EXIST ;
	return node->node_data_s_ptr;
}

long GetDomNodeData_long( struct DomNode *node , enum DomNodeExist *is_exist )
{
	if( node->node_type != DOMNODE_TYPE_LONG )
	{
		if( is_exist )
			(*is_exist) = DOMNODE_NODE_TYPE_NOT_MATCHED ;
		return 0;
	}
	
	if( node->is_node_data_exist == 0 )
	{
		if( is_exist )
			(*is_exist) = DOMNODE_NODE_DATA_NOT_EXIST ;
		return 0;
	}
	
	if( is_exist )
		(*is_exist) = DOMNODE_NODE_DATA_EXIST ;
	return node->node_data.l_buf;
}

double GetDomNodeData_double( struct DomNode *node , enum DomNodeExist *is_exist )
{
	if( node->node_type != DOMNODE_TYPE_DOUBLE )
	{
		if( is_exist )
			(*is_exist) = DOMNODE_NODE_TYPE_NOT_MATCHED ;
		return 0.00;
	}
	
	if( node->is_node_data_exist == 0 )
	{
		if( is_exist )
			(*is_exist) = DOMNODE_NODE_DATA_NOT_EXIST ;
		return 0.00;
	}
	
	if( is_exist )
		(*is_exist) = DOMNODE_NODE_DATA_EXIST ;
	return node->node_data.d_buf;
}

unsigned char GetDomNodeData_bool( struct DomNode *node , enum DomNodeExist *is_exist )
{
	if( node->node_type != DOMNODE_TYPE_BOOL )
	{
		if( is_exist )
			(*is_exist) = DOMNODE_NODE_TYPE_NOT_MATCHED ;
		return 0;
	}
	
	if( node->is_node_data_exist == 0 )
	{
		if( is_exist )
			(*is_exist) = DOMNODE_NODE_DATA_NOT_EXIST ;
		return 0;
	}
	
	if( is_exist )
		(*is_exist) = DOMNODE_NODE_DATA_EXIST ;
	return node->node_data.b_buf;
}

char *FindChildDomNodeData_string( struct DomNode *parent_node , char *node_name , enum DomNodeExist *is_exist )
{
	struct DomNode		*node = NULL ;
	
	node = FindChildDomNode( parent_node , node_name ) ;
	if( node == NULL )
	{
		if( is_exist )
			(*is_exist) = DOMNODE_NODE_NOT_EXIST ;
		return NULL;
	}
	
	return GetDomNodeData_string( node , is_exist );
}

long FindChildDomNodeData_long( struct DomNode *parent_node , char *node_name , enum DomNodeExist *is_exist )
{
	struct DomNode		*node = NULL ;
	
	node = FindChildDomNode( parent_node , node_name ) ;
	if( node == NULL )
	{
		if( is_exist )
			(*is_exist) = DOMNODE_NODE_NOT_EXIST ;
		return 0;
	}
	
	return GetDomNodeData_long( node , is_exist );
}

double FindChildDomNodeData_double( struct DomNode *parent_node , char *node_name , enum DomNodeExist *is_exist )
{
	struct DomNode		*node = NULL ;
	
	node = FindChildDomNode( parent_node , node_name ) ;
	if( node == NULL )
	{
		if( is_exist )
			(*is_exist) = DOMNODE_NODE_NOT_EXIST ;
		return 0.00;
	}
	
	return GetDomNodeData_double( node , is_exist );
}

unsigned char FindChildDomNodeData_bool( struct DomNode *parent_node , char *node_name , enum DomNodeExist *is_exist )
{
	struct DomNode		*node = NULL ;
	
	node = FindChildDomNode( parent_node , node_name ) ;
	if( node == NULL )
	{
		if( is_exist )
			(*is_exist) = DOMNODE_NODE_NOT_EXIST ;
		return 0;
	}
	
	return GetDomNodeData_bool( node , is_exist );
}

struct DomNode *LinkXmlPropertyDomNode( struct DomNode *node , struct DomNode *prop_node )
{
	if( node == NULL || prop_node == NULL )
		return NULL;
	
	if( node->xml_properties == NULL )
	{
		prop_node->parent_node = NULL ;
		prop_node->prev_node = NULL ;
		prop_node->next_node = NULL ;
		node->xml_properties = prop_node ;
		return prop_node;
	}
	else
	{
		LinkSiblingDomNode( node->xml_properties , prop_node );
		return prop_node;
	}
}

struct DomNode *GetFirstXmlPropertyDomNode( struct DomNode *node )
{
	if( node == NULL )
		return NULL;
	
	return node->xml_properties;
}

struct DomNode *GetNextXmlPropertyDomNode( struct DomNode *node )
{
	if( node == NULL )
		return NULL;
	
	return node->next_node;
}

struct DomNode *FindXmlPropertyDomNode( struct DomNode *node , char *prop_name )
{
	if( node == NULL || prop_name == NULL )
		return NULL;
	
	return FindSiblingDomNode( node->xml_properties , prop_name ) ;
}

struct DomNode *AddXmlPropertyDomNode_string( struct DomNode *node , char *prop_name , int prop_name_len , char *s , int len )
{
	return LinkXmlPropertyDomNode( node , CreateDomNode(prop_name,prop_name_len,DOMNODE_TYPE_STRING,s,len) );
}

struct DomNode *AddXmlPropertyDomNode_long( struct DomNode *node , char *prop_name , int prop_name_len , long l )
{
	return LinkXmlPropertyDomNode( node , CreateDomNode(prop_name,prop_name_len,DOMNODE_TYPE_LONG,&l,-1) );
}

struct DomNode *AddXmlPropertyDomNode_double( struct DomNode *node , char *prop_name , int prop_name_len , double d )
{
	return LinkXmlPropertyDomNode( node , CreateDomNode(prop_name,prop_name_len,DOMNODE_TYPE_DOUBLE,&d,-1) );
}

struct DomNode *AddXmlPropertyDomNode_bool( struct DomNode *node , char *prop_name , int prop_name_len , unsigned char b )
{
	return LinkXmlPropertyDomNode( node , CreateDomNode(prop_name,prop_name_len,DOMNODE_TYPE_BOOL,&b,-1) );
}

char *FindXmlPropertyDomNode_string( struct DomNode *node , char *prop_name , enum DomNodeExist *is_exist )
{
	struct DomNode		*prop_node = NULL ;
	
	prop_node = FindXmlPropertyDomNode( node , prop_name ) ;
	if( prop_node == NULL )
	{
		if( is_exist )
			(*is_exist) = DOMNODE_NODE_NOT_EXIST ;
		return NULL;
	}
	
	return GetDomNodeData_string( prop_node , is_exist );
}

long FindXmlPropertyDomNode_long( struct DomNode *node , char *prop_name , enum DomNodeExist *is_exist )
{
	struct DomNode		*prop_node = NULL ;
	
	prop_node = FindXmlPropertyDomNode( node , prop_name ) ;
	if( prop_node == NULL )
	{
		if( is_exist )
			(*is_exist) = DOMNODE_NODE_NOT_EXIST ;
		return 0;
	}
	
	return GetDomNodeData_long( prop_node , is_exist );
}

double FindXmlPropertyDomNode_double( struct DomNode *node , char *prop_name , enum DomNodeExist *is_exist )
{
	struct DomNode		*prop_node = NULL ;
	
	prop_node = FindXmlPropertyDomNode( node , prop_name ) ;
	if( prop_node == NULL )
	{
		if( is_exist )
			(*is_exist) = DOMNODE_NODE_NOT_EXIST ;
		return 0.00;
	}
	
	return GetDomNodeData_double( prop_node , is_exist );
}

unsigned char FindXmlPropertyDomNode_bool( struct DomNode *node , char *prop_name , enum DomNodeExist *is_exist )
{
	struct DomNode		*prop_node = NULL ;
	
	prop_node = FindXmlPropertyDomNode( node , prop_name ) ;
	if( prop_node == NULL )
	{
		if( is_exist )
			(*is_exist) = DOMNODE_NODE_NOT_EXIST ;
		return 0;
	}
	
	return GetDomNodeData_bool( prop_node , is_exist );
}

struct DomNode *CreateXmlCdataDomNode( char *node_name , int node_name_len , void *node_data , int node_data_len )
{
	struct DomNode	*node ;
	
	node = CreateDomNode( node_name , node_name_len , DOMNODE_TYPE_STRING , node_data , node_data_len ) ;
	if( node == NULL )
		return NULL;
	
	node->is_xml_cdata_node = 1 ;
	
	return node;
}

unsigned char IsXmlCdataDomNode( struct DomNode *node )
{
	return node->is_xml_cdata_node;
}

void SetParserLastError( int error )
{
	g_paster_last_error = error ;
	
	return;
}

int GetParserLastError()
{
	return g_paster_last_error;
}

